package com.yfbao.horizon.user.injection.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.util.Histogram;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.binder.MeterBinder;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.shardingjdbc.jdbc.core.context.ShardingRuntimeContext;
import org.apache.shardingsphere.shardingjdbc.jdbc.core.datasource.ShardingDataSource;
import org.apache.shardingsphere.sql.parser.binder.metadata.index.IndexMetaData;
import org.apache.shardingsphere.sql.parser.binder.metadata.table.TableMetaData;
import org.apache.shardingsphere.transaction.spi.ShardingTransactionManager;
import org.apache.shardingsphere.underlying.common.metadata.ShardingSphereMetaData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.sql.DatabaseMetaData;
import java.util.Collection;
import java.util.Map;

@Slf4j
@Configuration
public class ShardingMetrics implements MeterBinder {
    @Autowired
    private DataSource dataSource;

    @Override
    public void bindTo(MeterRegistry registry) {
        if(dataSource instanceof ShardingDataSource){
            ShardingDataSource shardingDataSource = (ShardingDataSource) this.dataSource;
            Map<String, DataSource> dataSourceMap = shardingDataSource.getDataSourceMap();
            Gauge.builder("sharding.datasource.count",dataSourceMap,Map::size)
                    .tag("sharding","datasources")
                    .description("分库分表数据源数量")
                    .register(registry);

            for (Map.Entry<String, DataSource> dataSourceEntry : dataSourceMap.entrySet()) {
                String key = dataSourceEntry.getKey();
                DataSource value = dataSourceEntry.getValue();
                log.info("数据源名: " + key);
                if(value instanceof DruidDataSource){
                    DruidDataSource druidDataSource =   (DruidDataSource) value;
                    metricDruidDataSource(druidDataSource,registry,key);
                }
            }
            ShardingSphereMetaData metaData = shardingDataSource.getRuntimeContext().getMetaData();
            Collection<String> allTableNames = metaData.getSchema().getAllTableNames();
            Gauge.builder("sharding.tables.count",allTableNames,Collection::size).tag("sharding","tables")
                    .description("分库分表的表数量")
                    .register(registry);

            for (String allTableName : allTableNames) {
                TableMetaData tableMetaData = metaData.getSchema().get(allTableName);
                Map<String, IndexMetaData> indexes = tableMetaData.getIndexes();
                log.info("逻辑表: " + allTableName + ", 分片数量: " + indexes.size());
            }

            log.info("分片表数量: " + allTableNames.size());
//            DatabaseMetaData metaData = shardingDataSource.getConnection().getMetaData();
//            ShardingTransactionManager shardingTransactionManager = shardingDataSource.getConnection().getShardingTransactionManager();

        }

    }

    private void metricDruidDataSource(DruidDataSource druidDataSource,MeterRegistry meterRegistry, String tag){
        Gauge.builder("druid.datasource.activeCount", druidDataSource, DruidDataSource::getActiveCount)
                .tag("sharding-datasource",tag)
                .description("当前活跃的连接数")
                .register(meterRegistry);

        Gauge.builder("druid.datasource.poolingCount", druidDataSource, DruidDataSource::getPoolingCount)
                .tag("sharding-datasource",tag)
                .description("当前空闲的连接数")
                .register(meterRegistry);

        Gauge.builder("druid.datasource.connectCount", druidDataSource, DruidDataSource::getConnectCount)
                .tag("sharding-datasource",tag)
                .description("累计获取的连接数")
                .register(meterRegistry);

        Gauge.builder("druid.datasource.closeCount", druidDataSource, DruidDataSource::getCloseCount)
                .tag("sharding-datasource",tag)
                .description("关闭连接的次数")
                .register(meterRegistry);

        Gauge.builder("druid.datasource.waitingCount", druidDataSource, DruidDataSource::getWaitThreadCount)
                .tag("sharding-datasource",tag)
                .description("等待获取连接的线程数量")
                .register(meterRegistry);
        Histogram transactionHistogram1 = druidDataSource.getTransactionHistogram();
//            transactionHistogram.getSum()
        long[] transactionHistogram = transactionHistogram1.getRanges();
        // 暴露每个时间段的事务数量
        Gauge.builder("druid.transactions.0_1ms", () -> transactionHistogram[0])
                .tag("sharding-datasource",tag)
                .description("事务执行耗时在0-1ms至间的数量")
                .register(meterRegistry);

        Gauge.builder("druid.transactions.1_10ms", () -> transactionHistogram[1])
                .tag("sharding-datasource",tag)
                .description("事务执行耗时在1-10ms至间的数量")
                .register(meterRegistry);

        Gauge.builder("druid.transactions.10_100ms", () -> transactionHistogram[2])
                .tag("sharding-datasource",tag)
                .description("事务执行耗时在10-100ms至间的数量")
                .register(meterRegistry);

        Gauge.builder("druid.transactions.100_1000ms", () -> transactionHistogram[3])
                .tag("sharding-datasource",tag)
                .description("事务执行耗时在100-1000ms至间的数量")
                .register(meterRegistry);

        Gauge.builder("druid.transactions.1000ms_plus", () -> transactionHistogram[4])
                .tag("sharding-datasource",tag)
                .description("事务执行耗时超过1000ms的数量")
                .register(meterRegistry);
    }
}
